/**
 * Copyright 2000 self.com.au
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License. (http://www.apache.org/licenses/LICENSE-2.0)
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed 
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for 
 * the specific language governing permissions and limitations under the License.
 *
 * <dhamma-message src="Atisha (11th century Tibetan Buddhist master)">
 * 	The greatest achievement is selflessness. The greatest worth is self-mastery.
 * 	The greatest quality is seeking to serve others. The greatest precept is continual awareness.
 * 	The greatest medicine is the emptiness of everything. The greatest action is not conforming with the worlds ways.
 * 	The greatest magic is transmuting the passions. The greatest generosity is non-attachment.
 * 	The greatest goodness is a peaceful mind. The greatest patience is humility.
 * 	The greatest effort is not concerned with results. The greatest meditation is a mind that lets go.
 * 	The greatest wisdom is seeing through appearances. 
 * </dhamma-message>
 *
 * @author Ashin Wimalajeewa
 */
package self.amigo.diag;

import java.awt.*;
import java.io.*;
import java.text.*;
import java.util.*;

import org.w3c.dom.*;

import self.gee.*;
import self.awt.*;
import self.lang.*;
import self.xml.*;

import self.amigo.diag.layer.*;

public class BasicDiagram implements IDiagram, Serializable {
	
	/**
	 * If the internal state of this class ever changes in such a way that it can't be defaulted,
	 * then the {@link #serialVersionUID} should be incremented to ensure serialized instances cleanly fail.  
	 */
	private static final long serialVersionUID = 1;
	
  final static private Font   DEFAULT_FONT = new Font("Arial", Font.PLAIN, 9);
  final static public  String DIAGRAM_LAYER_PROP = "DiagramLayer";
  final static public  String TITLE_PROP = "title";
  final static public  String WIDTH_PROP = "width";
  final static public  String HEIGHT_PROP = "height";
  final static public  String FONT_PROP = "font";
  final static public  String LASTSAVED_PROP = "lastsaved";
  final static public  String MODIFIED_PROP = "modified";

  final static private String UNSAVED = "unsaved";

  private String          title = "untitled";
  private String          lastSaved = UNSAVED;
  private int             width = 600;
  private int             height = 400;
  private Font            font = new Font( DEFAULT_FONT.getAttributes() );

  private long            lastSavedTime = -1;
  private boolean         isDirty = false;

  private boolean cacheUpdates = true;
  private int     updateStack = 0;

  private BasicDiagramLayer   core; // need to reassign elements

  transient private IDiagramCanvas  canvas;
  transient private ILayer          background;
  transient private ILayer          foreground;

  public BasicDiagram() {
    core = new BasicDiagramLayer( this );
  }

  public void setTitle( String name ) {
    title = name;
  }

  public String getTitle() {
    return title;
  }

  public int getWidth() {
    return width;
  }

  public int getHeight() {
    return height;
  }

  public String getLastSaved() {
    return lastSaved;
  }

  public Font getFont() {
    return font;
  }

  public boolean isModified() {
    return isDirty;
  }

  public void setDirty() {
    isDirty = true;
  }

  public IDiagramLayer getContext() {
    return core;
  }

  public void prepareSurface( IDiagramCanvas drawingSurface ) {
    canvas = drawingSurface;
    canvas.setPlotter( this );
  }

  public void removeCanvas() {
    if ( canvas != null )
      canvas.setPlotter( null );
    canvas = null;
  }

  public void dispose() {
    removeCanvas();
    background = null;
    foreground = null;
    core.dispose();
    core = null;
  }

  public void plot(  Graphics rawCanvas ) {
    if ( (background != null) && background.isVisible() )
      background.paint( rawCanvas );
    core.paint( rawCanvas );
    if ( (foreground != null) && foreground.isVisible() )
      foreground.paint( rawCanvas );
  }

  public ILayer getBackground() {
    return background;
  }

  public ILayer getForeground() {
    return foreground;
  }

  public void setBackground( ILayer background ) {
    this.background = background;
  }

  public void setForeground( ILayer foreground ) {
    this.foreground = foreground;
  }

  public void beginUpdate() {
    if ( cacheUpdates )
      updateStack++;
  }

  public void endUpdate() {
    if ( cacheUpdates ) {
      updateStack--;
      if ( updateStack == 0 )
        redraw();
    }
  }

  public void redraw() {
    if ( canvas != null )
      canvas.redraw();
  }

  public IDiagram deepClone() {
    return (IDiagram) ObjectUtils.deepClone( this );
  }

  public void assign( IDiagram src ) {
    if ( src == null )
      throw new IllegalArgumentException( "err.invalid.assignment.diagram" );
    if ( !(src instanceof BasicDiagram) )
      throw new IllegalArgumentException( "err.invalid.assignment.diagram" );
    BasicDiagram other = (BasicDiagram) src;

    title = other.title;
    lastSaved = other.lastSaved;
    lastSavedTime = other.lastSavedTime-1;
    font = other.font;

    java.util.List elemStore = new ArrayList();
    other.getContext().getElements( elemStore );
    core.assignElements( elemStore );

    isDirty = other.isDirty;
  }

  // IEditableElement
  public void       getProperties( Map store ) {
    store.put( TITLE_PROP, title );
    store.put( WIDTH_PROP, new Integer(width) );
    store.put( HEIGHT_PROP, new Integer(height) );
    store.put( LASTSAVED_PROP, getLastSaved() );
    store.put( MODIFIED_PROP, BooleanUtils.toBoolean(isModified()) );
    store.put( FONT_PROP, font );
  }

  public void       setProperties( Map data ) {
    title = (String) data.get( TITLE_PROP );
    width = ((Integer) data.get(WIDTH_PROP)).intValue();
    height = ((Integer) data.get(HEIGHT_PROP)).intValue();
    font = (Font) data.get( FONT_PROP );
    setDirty();
  }

  // IPersistentElement
  public void readFrom( Node self, HashMap idObjLookUp ) throws DOMException {
    title = DomUtils.getElementAttribute( self, TITLE_PROP );
    String l = DomUtils.getElementAttribute( self, LASTSAVED_PROP );
    lastSavedTime = Long.parseLong( l );
    setDateTimeFrom();
    try {
      width = Integer.parseInt( DomUtils.getElementAttribute(self, WIDTH_PROP) );
    } catch ( Exception err ) {
    	// nothing to do.
    }
    try {
      height = Integer.parseInt( DomUtils.getElementAttribute(self, HEIGHT_PROP) );
    } catch ( Exception err ) {
    	// nothing to do.
    }
    try {
      String fontAsString = DomUtils.getElementAttribute( self, FONT_PROP );
      font = Font.decode( fontAsString );
    } catch ( Exception err ) {
    	// nothing to do.
    }
    Node diagLayer = DomUtils.getChildElementNode( self, DIAGRAM_LAYER_PROP );
    core.readFrom( diagLayer, idObjLookUp );
    isDirty = false;
  }

  public void writeTo( Document doc, Element self, HashMap objIdLookup ) throws DOMException {
    updateStack = 0;
    lastSavedTime = System.currentTimeMillis();
    DomUtils.setElementAttribute( self, TITLE_PROP, StringUtils.toEmptyIfNull(title) );
    DomUtils.setElementAttribute( self, LASTSAVED_PROP, "" + lastSavedTime );
    if ( width != 600 )
      DomUtils.setElementAttribute( self, WIDTH_PROP, "" + width );
    if ( height != 400 )
      DomUtils.setElementAttribute( self, HEIGHT_PROP, "" + height );
    if ( !DEFAULT_FONT.equals(font) ) {
      String fontAsString = FontUtils.encode( font );
      DomUtils.setElementAttribute( self, FONT_PROP, StringUtils.toEmptyIfNull(fontAsString) );
    }
    Element diagLayer = doc.createElement( DIAGRAM_LAYER_PROP );
    self.appendChild( diagLayer );
    isDirty = false;
    core.writeTo( doc, diagLayer, objIdLookup );
    setDateTimeFrom();
  }

  private void setDateTimeFrom() {
    if ( lastSavedTime == -1 )
      lastSaved = UNSAVED;
    Date saved = new Date( lastSavedTime );
    DateFormat dateFormat = DateFormat.getDateInstance( DateFormat.MEDIUM );
    DateFormat timeFormat = DateFormat.getTimeInstance( DateFormat.SHORT );
    lastSaved =  dateFormat.format(saved) + " " + timeFormat.format(saved);
  }
}
